* Forms based nginx login and pluggable shiro auth in karaf

My usecase was this: I had a set of small [[http://ops4j.github.io/pax/web/SNAPSHOT/User-Guide.html#whiteboard-extender][OSGi web whiteboard]] web applications running in [[https://karaf.apache.org][apache karaf]] fronted by [[https://nginx.org][nginx]] and I wanted to have the same login and set of users across all web application, and I wanted to have the same forms based for nginx as well.  A sort of "poor man's single signon".

This project was my solution.

This project contains a set of [[https://karaf.apache.org/manual/latest/#_feature_and_resolver][apache karaf features]] that fills two purposes
 1. Providing a [[Forms based login for nginx][forms based login mechanism for nginx]] (Note: the webapp provides only authentication. No authorization of individual URLs. All authenticated users get in)
 2. Providing a "poor man's single sign-on" for web applications running in the same apache karaf instance

** Relationship with apache shiro

This project is an application that uses [[https://shiro.apache.org][apache shiro]] to provide authentication and authorization.

Authservice creates a JDBC database for a [[https://shiro.apache.org/realm.html][shiro realm]] with users, roles and permission, and an admin GUI to adminstrate the database content.

It also creates a simple self-service GUI for logged in users to change their user info (name, email) and for users to modify their own passwords.

** Status of the project

[[https://github.com/steinarb/authservice/actions/workflows/authservice-maven-ci-build.yml][file:https://github.com/steinarb/authservice/actions/workflows/authservice-maven-ci-build.yml/badge.svg]]
[[https://coveralls.io/github/steinarb/authservice][file:https://coveralls.io/repos/github/steinarb/authservice/badge.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=alert_status#.svg]]
[[https://maven-badges.herokuapp.com/maven-central/no.priv.bang.authservice/authservice][file:https://maven-badges.herokuapp.com/maven-central/no.priv.bang.authservice/authservice/badge.svg]]
[[https://www.javadoc.io/doc/no.priv.bang.authservice/authservice-parent][file:https://www.javadoc.io/badge/no.priv.bang.authservice/authservice-parent.svg]]

[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/images/project_badges/sonarcloud-white.svg]]

[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=sqale_index#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=coverage#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=ncloc#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=code_smells#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=sqale_rating#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=security_rating#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=bugs#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=vulnerabilities#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=duplicated_lines_density#.svg]]
[[https://sonarcloud.io/summary/new_code?id=steinarb_authservice][file:https://sonarcloud.io/api/project_badges/measure?project=steinarb_authservice&metric=reliability_rating#.svg]]

** Installing on karaf
/Note/: The instructions here don't describe a production enviroment, but they describe setting up something that will let the service be startet.

The webapp needs PostgreSQL running, with a database named "ukelonn" containing the table users, and a no-password authentication scheme.

Instructions:
 1. In bash, clone and build the authservice app:
    #+BEGIN_EXAMPLE
      mkdir -p ~/git/
      cd ~/git/
      git clone https://github.com/steinarb/authservice.git
      cd ~/git/authservice/
      mvn clean install
    #+END_EXAMPLE
 2. [[https://karaf.apache.org/manual/latest/quick-start.html][Follow the quick start guide to downloading, unpacking and starting apache karaf]]
 3. In the karaf shell, install the authservice feature repository
    #+BEGIN_EXAMPLE
      feature:repo-add mvn:no.priv.bang.authservice/karaf/LATEST/xml/features
    #+END_EXAMPLE
 4. In the karaf shell, install the feature that installs the authorization service that is used by nginx (this feature installs a set of test users, roles and features)
    #+BEGIN_EXAMPLE
      feature:install authservice-with-testdb-dbrealm-and-session
    #+END_EXAMPLE
 5. Open a browser on the URL http://localhost:8181/authservice and do a login with a valid username/password combination (e.g. "admin/admin")
 6. Open a browser on the URL http://localhost:8181/authservice/check and verify that it doesn't return a 401 HTTP code
 7. Optionally install the user administration UI (not needed for using this service with nginx, but needed for administrating the access)
    #+BEGIN_EXAMPLE
      feature:install user-admin-with-testdb
    #+END_EXAMPLE
 8. Open a browser on the URL http://localhost:8181/authservice/useradmin and test adding/modifying users, roles and permissions

** Configuration
*** Configuring rememberme timeout

Shiro rememberme sessions by default last for 30 minutes (1800000 milliseconds).

If you would like a longer lifetime for the sessions, say e.g. 7 days, then you would need to:
 1. Find the number of milliseconds in 7 days
    #+begin_example
      numberOfMillisInASecond = 1000
      numberOfMillisInAMinute = numberOfMillisInASecond * 60 = 60000
      numberOfMillisInAnHour = numberOfMillisInAMinute * 60 = 3600000
      numberOfMillisInADay = numberOfMillisInAnHour * 24 = 86400000
      numberOfMillisInADay * 7 = 604800000
    #+end_example
 2. Set the rememberme timeout configuration by doing the following commands from the karaf console command line:
    #+begin_example
      config:edit no.priv.bang.authservice.web.security.shiroconfig.ShiroConfigServiceProvider
      config:property-set globalSessionTimeout 604800000
      config:update
    #+end_example

** Forms based login for nginx

The webapp installed by the above installation instructions offers two URLs for use by the [[http://nginx.org/en/docs/http/ngx_http_auth_request_module.html][NGINX auth_request module]]:
 - /auth which will just check the login state of Apache Shiro, returning the status code 401 for failure and 200 for success
 - /login which contains a login form and will authenticate against Apache Shiro

The webapp is implemented as two servlets exposed as OSGi services, that will be picked up by the [[http://ops4j.github.io/pax/web/4.x/index.html#_whiteboard_extender][pax web whiteboard extender]].

*** Installing and configuring nginx

Instructions:
 1. Install nginx with the auth module.  On debian this is done with the command
    #+BEGIN_EXAMPLE
      apt-get update
      apt-get install nginx-extras
    #+END_EXAMPLE
 2. Add the following to the /etc/nginx/sites-available/default (adapt this to the actual server/site in use):
    #+BEGIN_SRC conf
      server {
              listen 80 default_server;
              listen [::]:80 default_server;

              root /var/www/html;

              # Add index.php to the list if you are using PHP
              index index.html index.htm index.nginx-debian.html;

              server_name _;

              location /authservice {
                      auth_request off; # Necessary for REST API POST to work, shiro will handle authorization here
                      proxy_pass http://localhost:8181/authservice;
                      proxy_cookie_path ~^/authservice.*$ /;
                      proxy_set_header Host $host;
              }

              # Avoid browser attempt at fetching favicon.ico triggering a login and redirecting
              # a 404 Not Found when there is no favicon.ico on the site (which is perferctly OK
              # for both the site and the browser)
              location /favicon.ico {
                      auth_request off;
              }

               location / {
                      # First attempt to serve request as file, then
                      # as directory, then fall back to displaying a 404.
                      try_files $uri $uri/ =404;
              }

              # Auth configuration
              auth_request /authservice/check;
              error_page 401 = @error401;

              # If the user is not logged in, redirect to authservice login URL, with redirect information
              location @error401 {
                      add_header X-Original-URI "$scheme://$http_host$request_uri";
                      add_header Set-Cookie "NSREDIRECT=$scheme://$http_host$request_uri";
                      return 302 /authservice/login?originalUri=$scheme://$http_host$request_uri;
               }
      }
    #+END_SRC
*** Installing and configuring postgresql
/Note/: only command examples for debian/ubuntu/etc. are shown, but the overall steps should work on a lot of platforms
 1. Install PostgreSQL, as root do the following command:
    #+BEGIN_EXAMPLE
      apt-get install postgresql
    #+END_EXAMPLE
 2. Add a PostgreSQL user named "karaf", as root do the following command
    #+BEGIN_EXAMPLE
      PGPASSWORD=karaf sudo -u postgres createuser karaf
    #+END_EXAMPLE
    /Note/: Replace the password in the PGPASSWORD environment variable with something other than the example and use that password in the karaf configuration
 3. Create an empty PostgreSQL database named "authservice" owned by user "karaf"
    #+BEGIN_EXAMPLE
      sudo -u postgres createdb -O karaf authservice
    #+END_EXAMPLE
*** Installing and configuring apache karaf
Instructions:
 1. Install apache karaf as a service, either [[http://karaf.apache.org/manual/latest/#_service_script_templates][using the karaf installation scripts]] or by [[https://steinar.bang.priv.no/2018/01/23/installing-apache-karaf-on-debian/][using apt-get and the unofficial karaf deb package]]
 2. SSH in to the karaf console:
    #+BEGIN_EXAMPLE
       ssh -p 8101 karaf@localhost
    #+END_EXAMPLE
    The default password is "karaf" (without the quotes).  It might be a good idea to change this.  See the karaf documentation for how to change the password
 3. In the karaf console, do the following:
    1. Add connection configuration for the postgresql database:
       #+BEGIN_EXAMPLE
         config:edit org.ops4j.datasource-authservice-production
         config:property-set osgi.jdbc.driver.name "PostgreSQL JDBC Driver"
         config:property-set dataSourceName "jdbc/authservice"
         config:property-set ops4j.preHook "authservicedb"
         config:property-set org.apache.karaf.features.configKey "org.ops4j.datasource-authservice-production"
         config:property-set url "jdbc:postgresql:///authservice"
         config:property-set user "karaf"
         config:property-set password "karaf"
         config:update
       #+END_EXAMPLE
       /Note/: use the actual password given in the PGPASSWORD environment variable when creating the karaf user
    2. Install authservice from maven central:
       #+BEGIN_EXAMPLE
         feature:repo-add mvn:no.priv.bang.authservice/karaf/LATEST/xml/features
         feature:install user-admin-with-productiondb
       #+END_EXAMPLE
 4. Open a the nginx authservice URL in a web browser, e.g. https://myserver.com/authservice/ and:
    1. Log in as user "admin" with password "admin" (without the quotes)
    2. Click on the "User administration UI" link
    3. In the administration UI:
       1. Click on "Administrate users"
       2. Change the password of user "admin"
       3. Add users that are to be able to log in to nginx
          /Note/: The nginx config provides only authentication for nginx, no authorization based on the combination of path and role or permission.  Therefore there is no need to add roles to users that only needs to log in
          Users that need to administrate other users, should get the useradmin role
    4. Add some links to the selfservice URLs from your website's top page (or whereever is convenient):
       1. Change password: https://myserver.com/authservice/password/
       2. Modify real namd and email: https://myserver.com/authservice/user
** Integrating with a Declarative Services (DS) web whiteboard application in karaf
/Note/: only command examples for debian/ubuntu/etc. are shown, but the overall steps should work on a lot of platforms

Do the following steps:
 1. Install PostgreSQL, as root do the following command:
    #+BEGIN_EXAMPLE
      apt-get install postgresql
    #+END_EXAMPLE
 2. Add a PostgreSQL user named "karaf", as root do the following command
    #+BEGIN_EXAMPLE
      PGPASSWORD=karaf sudo -u postgres createuser karaf
    #+END_EXAMPLE
    /Note/: Replace the password in the PGPASSWORD environment variable with something other than the example and use that password in the karaf configuration
 3. Create an empty PostgreSQL database named "authservice" owned by user "karaf"
    #+BEGIN_EXAMPLE
      sudo -u postgres createdb -O karaf authservice
    #+END_EXAMPLE
 4. SSH into the karaf console and add connection configuration for the postgresql database with the following commands:
    #+BEGIN_EXAMPLE
      config:edit org.ops4j.datasource-authservice-production
      config:property-set osgi.jdbc.driver.name "PostgreSQL JDBC Driver"
      config:property-set dataSourceName "jdbc/authservice"
      config:property-set ops4j.preHook "authservicedb"
      config:property-set url "jdbc:postgresql:///authservice"
      config:property-set user "karaf"
      config:property-set password "karaf"
      config:update
    #+END_EXAMPLE
    /Note/: use the actual password given in the PGPASSWORD environment variable when creating the karaf user
 5. Create a new DS component maven project, containing
    1. A src/main/feature/feature.xml template file, referencing the authservice feature repository and the authservice feature, e.g.:
       #+BEGIN_SRC nxml
         <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
         <features xmlns="http://karaf.apache.org/xmlns/features/v1.5.0" name="authservice.bundle">
             <repository>mvn:no.priv.bang.authservice/karaf/3.0.0/xml/features</repository>
             <feature name="${karaf-feature-name}">
                 <feature>user-admin-with-productiondb</feature>
             </feature>
         </features>
       #+END_SRC
    2. Add a shiro compile time dependency to the project's maven dependencies:
       #+BEGIN_EXAMPLE
         <dependency>
             <groupId>org.apache.shiro</groupId>
             <artifactId>shiro-core</artifactId>
             <version>1.3.2</version>
             <scope>provided</scope>
         </dependency>
       #+END_EXAMPLE
    3. A DS component exposing a ServletContextHelper service to the web whiteboard, e.g.:
       #+BEGIN_SRC java
         @Component(
             property= {
                 HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=ukelonn",
                 HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH+"=/ukelonn"},
             service=ServletContextHelper.class,
             immediate=true
         )
         public class UkelonnServletContextHelper extends ServletContextHelper { }
       #+END_SRC
    4. A DS component exposing a Filter service to the web whiteboard, extending the AbstractShiroFilter, requiring shiro Realm and SessionDAO OSGi service injections, and configured using code (the shiro.ini mechanism doesn't work well in OSGi), eg.:
       #+BEGIN_SRC java
         @Component(
             property= {
                 HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN+"=/*",
                 HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME +"=ukelonn)",
                 "servletNames=ukelonn"},
             service=Filter.class,
             immediate=true
         )
         public class UkelonnShiroFilter extends AbstractShiroFilter { // NOSONAR

             private Realm realm;
             private SessionDAO session;
             private static final Ini INI_FILE = new Ini();
             static {
                 // Can't use the Ini.fromResourcePath(String) method because it can't find "shiro.ini" on the classpath in an OSGi context
                 INI_FILE.load(UkelonnShiroFilter.class.getClassLoader().getResourceAsStream("shiro.ini"));
             }

             @Reference
             public void setRealm(Realm realm) {
                 this.realm = realm;
             }

             @Reference
             public void setSession(SessionDAO session) {
                 this.session = session;
             }

             @Activate
             public void activate() {
                 WebIniSecurityManagerFactory securityManagerFactory = new WebIniSecurityManagerFactory(INI_FILE);
                 DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) securityManagerFactory.createInstance();
                 DefaultWebSessionManager sessionmanager = new DefaultWebSessionManager();
                 sessionmanager.setSessionDAO(session);
                 securityManager.setSessionManager(sessionmanager);
                 setSecurityManager(securityManager);
                 securityManager.setRealm(realm);

                 IniFilterChainResolverFactory filterChainResolverFactory = new IniFilterChainResolverFactory(INI_FILE, securityManagerFactory.getBeans());
                 FilterChainResolver resolver = filterChainResolverFactory.createInstance();
                 setFilterChainResolver(resolver);
             }
         }
       #+END_SRC
    5. A shiro.ini resource containing a [urls] section providing access to various path, e.g:
       #+BEGIN_EXAMPLE
         [main]
         authc.loginUrl = /login

         [users]

         [urls]
         / = authc
         /user* = user
         /admin/** = roles[administrator]
         /api/login = anon
         /api/registerpayment = roles[administrator]
         /api/job/update = roles[administrator]
         /api/admin/** = roles[administrator]
         /api/** = authc
         /performedjobs = authc
         /performedpayments = authc
       #+END_EXAMPLE
    6. Something listening to the /login path inside the context provided by the WebContextHelper (i.e. /ukelonn/login in this example) and handling login.  "Something" could be a servlet or a JAX-RS resource.  An example of a JAX-RS resource to handle login, is this resource, which when receiving a GET returns an HTML page with a login form, and on receiving a POST from the form, performs the login:
       #+BEGIN_SRC java
         @Path("")
         public class LoginResource {

             @GET
             @Path("/login")
             @Produces(MediaType.TEXT_HTML)
             public InputStream getLogin() {
                 return getClass().getClassLoader().getResourceAsStream("web/login.html");
             }

             @POST
             @Path("/login")
             @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
             @Produces("text/html")
             public Response postLogin(@FormParam("username") String username, @FormParam("password") String password) {
                 Subject subject = SecurityUtils.getSubject();

                 UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray(), true);
                 try {
                     subject.login(token);

                     return Response.status(Response.Status.FOUND).entity("Login successful!").build();
                 } catch(UnknownAccountException e) {
                     return Response.status(Response.Status.UNAUTHORIZED).entity("Unknown account")).build();
                 } catch (IncorrectCredentialsException  e) {
                     return Response.status(Response.Status.UNAUTHORIZED).entity("Wrong password")).build();
                 } catch (LockedAccountException  e) {
                     return Response.status(Response.Status.UNAUTHORIZED).entity("Account is locked")).build();
                 } catch (AuthenticationException e) {
                     return Response.status(Response.Status.UNAUTHORIZED).entity("Unable to log in")).build();
                 } catch (Exception e) {
                     throw new InternalServerErrorException();
                 } finally {
                     token.clear();
                 }
             }
         }
       #+END_SRC
       /Note/! if the user logs in via the login form on the /authservice/ path on the same karaf server, the user will be logged into your application as well.
 6. A barebones DS component plugging into authservice, and that can be adapted to your project, can be found at [[https://github.com/steinarb/authservice-sampleclient][authservice-sampleclient]]

** Various ways of integrating with other webapps in karaf

There are several ways for a webapp to interact with authservice:
 1. Install authservice separately and add OSGi service injections for shiro Realm and Session (all user administration done in the authservice webapplication)
 2. Add the features for the liquibase database setup and the shiro Realm and Session and provide the necessary tables from a different web application's database
 3. Add the features for the authservice UserManagementService implementation, as well as the features for Realm and Session and and implement a user management GUI and webservice on top of the UserManagementService

...or various permutations of the above.  With [[https://github.com/steinarb/ukelonn][ukelonn]] I plan to add the authservice tables to the ukelonn database, and then let the ukelonn database provide the database for authservice itself.  I have made a first step in the direction of authservice integration by basing ukelonn's user management on the UserManagementService OSGi service, so that it later can be replaced by the authservice implementation of the service.

** Integrating with other databases than PostgreSQL and derby

Short story: it should be possible.  It should possible to use blank JDBC database that can be connected to with a combination of a JDBC url and username and password.

Currently authservice operates with two databases: an in-memory derby with mock data used for tests and development, and a PostgreSQL database used for production deployments.

Authservice uses XML syntax liquibase to set up the schema, straightforward SQL to insert initial data/mock data, and straightforward SQL to updated and query the authservice database, so replacing both derby and PostgreSQL, with other JDBC databases supported by liquibase (which is basically all of them), should be possible.

/Please note/ however, that none of the config below has been tested.

*** Replacing the test database (by default derby):
 Possible approach:
  1. In the karaf console, configure connection information to the alternative database ([[http://www.h2database.com/html/features.html#in_memory_databases][H2 in-memory database]] in the example):
     #+BEGIN_EXAMPLE
       config:edit org.ops4j.datasource-authservice-test
       config:property-set osgi.jdbc.driver.name "H2"
       config:property-set dataSourceName "jdbc/authservice"
       config:property-set ops4j.preHook "authservicedb"
       config:property-set url "jdbc:h2:mem:authservice"
       config:update
     #+END_EXAMPLE
  2. In the karaf console, install the H2 karaf feature (there is nothing in the authservice karaf features that loads this):
     #+BEGIN_EXAMPLE
       feature:install pax-jdbc-h2
     #+END_EXAMPLE
  3. Load the authservice feature repository:
     #+BEGIN_EXAMPLE
       feature:repo-add mvn:no.priv.bang.authservice/karaf/LATEST/xml/features
     #+END_EXAMPLE
  4. In the karaf console, load the test database authservice feature
     #+BEGIN_EXAMPLE
       feature:install authservice-with-testdb-dbrealm-and-session
     #+END_EXAMPLE
  5. Alternatively, load the test database user-admin feature (this is a superset of the authservice feature that adds a GUI for user management):
     #+BEGIN_EXAMPLE
       feature:install user-admin-with-testdb
     #+END_EXAMPLE

*** Replacing the production database (by default PostgreSQL):
 Possible approach:
  1. In the karaf console, configure connection information to the alternative database (MySQL in the example):
     #+BEGIN_EXAMPLE
       config:edit org.ops4j.datasource-authservice-production
       config:property-set osgi.jdbc.driver.name "mysql"
       config:property-set dataSourceName "jdbc/authservice"
       config:property-set ops4j.preHook "authservicedb"
       config:property-set url "jdbc:mysql://localhost/authservice"
       config:property-set user "karaf"
       config:property-set password "karaf"
       config:update
     #+END_EXAMPLE
  2. In the karaf console, install the H2 karaf feature (there is nothing in the authservice karaf features that loads this):
     #+BEGIN_EXAMPLE
       feature:install pax-jdbc-mysql
     #+END_EXAMPLE
  3. Load the authservice feature repository:
     #+BEGIN_EXAMPLE
       feature:repo-add mvn:no.priv.bang.authservice/karaf/LATEST/xml/features
     #+END_EXAMPLE
  4. In the karaf console, load the test database authservice feature
     #+BEGIN_EXAMPLE
       feature:install authservice-with-productiondb-dbrealm-and-session
     #+END_EXAMPLE
  5. Alternatively, load the test database user-admin feature (this is a superset of the authservice feature that adds a GUI for user management):
     #+BEGIN_EXAMPLE
       feature:install user-admin-with-productiondb
     #+END_EXAMPLE

** Release history

| Date         | Version | Comment                                                                                                                                               |
|--------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------|
| [2025-06-14] |   3.0.0 | Build for java 21 (reason for major version bump), use PostgreSQL 42.7.7                                                                              |
| [2025-05-17] |   2.4.0 | Add support for karaf config of rememberme session timeout                                                                                            |
| [2025-05-04] |   2.3.2 | Use current liquibase XSD (4.31), use jersey 2.46 and jackson 2.19.0                                                                                  |
| [2025-04-24] |   2.3.1 | shiro 2.0.4, bang-servlet 2.0.1 (which uses shiro 2.0.4)                                                                                              |
| [2025-04-18] |   2.3.0 | Add AuthserviceShiroFilterBase that sets up a shiro web environment from a shiro ini file                                                             |
| [2025-04-12] |   2.2.0 | Add CipherKeyService                                                                                                                                  |
| [2025-04-05] |   2.1.1 | Use liquibase 4.31.1                                                                                                                                  |
| [2025-03-02] |   2.1.0 | Use runtime incompatible bang-servlet 2.0.0 and authservice 2.1.0 (which uses bang-servlet 2.0.0)                                                     |
| [2025-02-19] |   2.0.6 | Use liquibase 4.31.0                                                                                                                                  |
| [2025-02-12] |   2.0.5 | Add support for locking users on failed login and unlocking users in admin UI, built with karaf 4.4.7, use postgresql jdbc 42.7.5                     |
| [2024-12-13] |   2.0.4 | Use liquibase 4.30.0, postgresql jdbc 42.7.4, shiro 2.0.2, bang-servlet 1.8.1                                                                         |
| [2024-08-03] |   2.0.3 | Use jersey 2.44 and jackson 2.17.2                                                                                                                    |
| [2024-07-31] |   2.0.2 | Use liquibase 4.29.0 and jsoup 1.18.1                                                                                                                 |
| [2024-07-04] |   2.0.1 | Use liquibase 4.28.0                                                                                                                                  |
| [2024-04-06] |   2.0.0 | Use shiro 2.0.1 and new record-based API of the UserService OSGi service                                                                              |
| [2024-04-06] | 1.15.20 | Use liquibase 4.27.0, build with node.js 20.12.0 and karaf 4.4.5                                                                                      |
| [2024-03-25] | 1.15.19 | Use postgresql jdbc 42.7.3, jersey 2.42, jackson 2.16.2                                                                                               |
| [2024-03-01] | 1.15.18 | Use postgresql jdbc 42.7.2                                                                                                                            |
| [2024-03-01] | 1.15.17 | Use version of JerseyServlet that supports shiro-jaxrs                                                                                                |
| [2024-02-12] | 1.15.16 | Shiro version set by maven parent, no longer export shiro deps in the BOM                                                                             |
| [2023-12-13] | 1.15.15 | Use shiro 1.13.0, postgresql JDBC 42.7.1, jsoup 1.17.1 and mockito 5.8.0                                                                              |
| [2023-12-12] | 1.15.14 | Use liquibase 4.24.0                                                                                                                                  |
| [2023-11-14] | 1.15.13 | junit jupiter 5.10.1, axios 1.6.1 .                                                                                                                   |
| [2023-11-05] | 1.15.12 | Use jersey 2.41, jackson 2.15.3, pax-jdbc 1.5.6, jsoup 1.16.2, junit jupiter 5.10.0, mockito 5.7.0, and mockrunner 2.0.7                              |
| [2023-10-31] | 1.15.11 | Use karaf 4.4.4 and updated npm dependencies                                                                                                          |
| [2023-07-30] | 1.15.10 | Use jersey 2.40 and jackson 2.15.2                                                                                                                    |
| [2023-07-08] |  1.15.9 | Use java 17                                                                                                                                           |
| [2023-07-01] |  1.15.8 | Use liquibase 4.23.0                                                                                                                                  |
| [2023-06-05] |  1.15.7 | Use shiro 1.11.0                                                                                                                                      |
| [2023-04-26] |  1.15.6 | Use jersey-karaf-feature 1.9.4 and jackson 2.15.0                                                                                                     |
| [2023-04-24] |  1.15.5 | Use jersey-karaf-feature 1.9.4, jersey 2.39.1 and jackson 2.14.2                                                                                      |
| [2023-04-23] |  1.15.4 | Avoid caching of user admin webapp                                                                                                                    |
| [2023-03-06] |  1.15.3 | Use liquibase 4.19.0, karaf 4.4.3, pax-jdbc 1.5.5, postgresql jdbc 42.5.4, jsoup 1.15.4, junit jupiter 5.9.2, mockito 5.1.1, assertj 3.24.2           |
| [2023-01-20] |  1.15.2 | Use bang servlet 1.6.4, frontend security fix                                                                                                         |
| [2022-12-09] |  1.15.1 | Fix broken BOM of authservice 1.15.0                                                                                                                  |
| [2022-12-08] |  1.15.0 | Use shiro 1.10.1                                                                                                                                      |
| [2022-11-28] |  1.14.8 | Use jersey 2.37, jackson 2.14.1 (fixes CVE-2022-42003 and CVE-2022-42004), and bang-servlet 1.6.3                                                     |
| [2022-11-26] |  1.14.7 | Use postgresql jdbc 42.5.1 to fix CVE-2022-41946, use jsoup 1.15.3 to fix CVE-2022-36033                                                              |
| [2022-11-01] |  1.14.6 | Use liquibase 4.17.1, postgresql jdbc driver 42.4.1 and upgrade of all upgradable frontend packages                                                   |
| [2022-08-21] |  1.14.5 | Use liquibase 4.15.0                                                                                                                                  |
| [2022-08-10] |  1.14.4 | Use jersey 2.36, karaf 4.4.1, maven-bundle-plugin 5.1.8, jsoup 1.15.2, junit jupiter 5.9.0, mockito 4.6.1, assertj 3.23.1                             |
| [2022-08-09] |  1.14.3 | Use jersey 2.36 and postgresql jdbc driver 42.4.1                                                                                                     |
| [2022-07-25] |  1.14.2 | Use karaf 4.4.1                                                                                                                                       |
| [2022-06-01] |  1.14.1 | Use jackson 2.13.3, bang-osgi-service 1.8.0 and bang-servlet 1.6.1                                                                                    |
| [2022-05-29] |  1.14.0 | Use karaf 4.4.0 and OSGi 8                                                                                                                            |
| [2022-02-21] | 1.13.12 | Use Java 11, karaf 4.3.6, postgresql JDBC 42.3.3, jersey 2.35, jackson 2.13.1 and node.js 16.14.0                                                     |
| [2021-10-13] | 1.13.11 | karaf 4.3.3, postgresql JDBC 4.2.24, junit jupiter 5.8.1, mockito 4.0.0 and assertj 3.21.0                                                            |
| [2021-09-30] | 1.13.10 | Use jsoup 1.14.3 and axios 0.21.4                                                                                                                     |
| [2021-07-25] |  1.13.9 | Use PostgreSQL JDBC driver 4.2.23                                                                                                                     |
| [2021-06-15] |  1.13.8 | Use jersey 2.34 and jackson 12.3                                                                                                                      |
| [2021-06-13] |  1.13.7 | Add shiro dependencies to the authservice BoM                                                                                                         |
| [2021-06-12] |  1.13.6 | Stop dependencyManagement version leakage from the authservice BoM                                                                                    |
| [2021-06-01] |  1.13.5 | Get OSGi 7 and OSGi 7 compendium versions from the karaf BoM                                                                                          |
| [2021-05-24] |  1.13.4 | Use eslint in frontend, upgrade frontend deps, use OSGi 7  web whiteboard annotations                                                                 |
| [2021-05-02] |  1.13.3 | servlet 1.5.4, bootstrap 4.6.0 and node.js 14.16.1                                                                                                    |
| [2021-04-19] |  1.13.2 | Provide a Bill of Materials to clients of the authserice                                                                                              |
| [2021-04-15] |  1.13.1 | Get maven dependencies and maven plugin config from a parent POM                                                                                      |
| [2021-04-12] |  1.13.0 | Built with karaf 4.3.0 and OSGi 7                                                                                                                     |
| [2021-03-21] |  1.12.3 | Get maven dependencies from the karaf 4.2.11 BoM                                                                                                      |
| [2021-03-14] |  1.12.2 | Use beans with builders from UserManagementService 1.6.1                                                                                              |
| [2021-01-24] |  1.12.1 | Use jersey 2.33 and JerseyServlet 1.4.0                                                                                                               |
| [2021-01-19] |  1.12.0 | Use shiro 1.7.0                                                                                                                                       |
| [2021-01-14] | 1.11.17 | fix build problems during release caused by the authservice.tests integration test                                                                    |
| [2021-01-14] | 1.11.16 | use axios 0.21.1 to fix github security alert, big rewrite of user admin frontend: use saga, redux simplification, use bootstrap navbar               |
| [2020-10-10] | 1.11.15 | Use PostgreSQL JDBC driver 42.2.17                                                                                                                    |
| [2020-09-26] | 1.11.14 | Use PostgreSQL JDBC driver 42.2.12                                                                                                                    |
| [2020-09-12] | 1.11.13 | Fix startup problem because InjectionFactory can't be found                                                                                           |
| [2020-09-11] | 1.11.12 | Remove servicemix javax.inject from maven build and runtime dependencies                                                                              |
| [2020-07-29] | 1.11.11 | Use version 1.2.3 of FrontendServlet and JerseyServlet, fix sonar issues, build with karaf 4.2.8                                                      |
| [2020-07-29] | 1.11.10 | Use version 1.2.0 of FrontendServlet and JerseyServlet                                                                                                |
| [2020-07-22] |  1.11.9 | Use liquibase 3.8.0                                                                                                                                   |
| [2020-04-10] |  1.11.7 | Use jersey 2.30.1 and jackson 2.10.3                                                                                                                  |
| [2020-04-01] |  1.11.6 | Remove the use of deprecated classes and methods in the shiro setup                                                                                   |
| [2020-03-26] |  1.11.5 | Use pax-web 7.2.14 (the version used by karaf 4.2.8)                                                                                                  |
| [2020-03-05] |  1.11.4 | Use runtime and compile dependency to jackson-databind 2.9.10.3 to fix security issue CVE-2020-8840                                                   |
| [2020-02-29] |  1.11.3 | Upgrade PostgreSQL JDBC to 42.2.10, react to 16.13.0, redux to 7.2.0, reduxjs toolkit to 1.2.5 and react-router to 5.1.2                              |
| [2020-02-27] |  1.11.2 | Uses [[https://github.com/steinarb/servlet#jersey][JerseyServlet]] to implement the REST API, no functional changes (but different runtime dependencies)                                              |
| [2020-02-24] |  1.11.1 | Use Shiro 1.5.1 to fix [[https://issues.apache.org/jira/browse/SHIRO-742][SHIRO-742]]                                                                                                                      |
| [2020-02-08] |  1.11.0 | Use Shiro 1.5.0 and the JdbcRealm with base64 encoded salt from Shiro 1.5.0 (/Note/! This version isn't usable because of [[https://issues.apache.org/jira/browse/SHIRO-742][SHIRO-742)]]                  |
| [2020-02-08] |  1.10.0 | Use jersey 2.30 and jackson 2.9.10.2 (/Note/! [[https://github.com/eclipse-ee4j/jersey/issues/4156][jersey 2.28 doesn't work on OSGi with JDK8]] so with JDK8 you need this version of authservice)           |
| [2020-01-14] |   1.9.0 | Use FrontendServlet to serve the react frontend and styling                                                                                           |
| [2019-12-31] |   1.8.0 | Let Immutable provide hashCode() and equals() implementation to user management beans                                                                 |
| [2019-12-07] |   1.7.1 | Move pax-jdbc-config from the master feature repository to the template feature.xml files of the liquibase PreHook maven modules                      |
| [2019-11-15] |   1.7.0 | Replace DatabaseService with pax-jdbc-config (opens for using other RDBMSes than PostgreSQL and derby)                                                |
| [2019-11-05] |   1.6.0 | Upgrade jackson to 2.9.10.1 to fix github security alert, use DataServiceBase                                                                         |
| [2019-10-16] |   1.5.4 | Use DatabaseService from osgi-service 1.3.0                                                                                                           |
| [2019-09-29] |   1.5.3 | Start authservice without updating liquibase schema if lock is held until liquibase lock timeout (5 minutes)                                          |
| [2019-09-25] |   1.5.2 | Upgrade jackson to 2.9.10 to fix github security alert                                                                                                |
| [2019-09-24] |   1.5.1 | Remove leftover reference to feature  postgresql-jdbc-karaf that broke feature loading in karaf                                                       |
| [2019-09-23] |   1.5.0 | Use PostgreSQL JDBC driver version 4.2.8, which has its own karaf feature                                                                             |
| [2019-08-02] |   1.4.0 | Better bootstrap styling of links, frontend version upgrades, PostgreSQL JDBC plugin that survives reloads, fix github security warning               |
| [2019-06-10] |   1.3.0 | Make authservice build with openjdk-11                                                                                                                |
| [2019-05-26] |   1.2.0 | Upgrade apache shiro to version 1.4.1 and upgrade jackson to version 2.9.9, fix webapp <title>                                                        |
| [2019-05-01] |   1.1.0 | useradmin frontend cleanup, update PostgreSQL driver to newest version (42.2.5), [[https://github.com/steinarb/authservice/issues/1][fix issue #1]] (PostgreSQL DataSource fails after JDBC bundle restart) |
| [2019-04-15] |   1.0.2 | Upgrade apache shiro from version 1.3.1 to version 1.3.2                                                                                              |
| [2019-04-12] |   1.0.1 | Avoid constraint name conflict from copy-paste from ukelonn liquibase schema, fix aggregate javadoc, ensure user with role useradmin always present   |
| [2019-04-02] |   1.0.0 | Initial release                                                                                                                                       |

** License

This software is licensed under Apache Public License v 2.0.

See the LICENSE file for the full details.
